package com.fulu.game.bi.service.impl.market;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.fulu.game.bi.common.exception.BizException;
import com.fulu.game.bi.common.util.csv.CsvExportUtil;
import com.fulu.game.bi.common.util.csv.Mapper;
import com.fulu.game.bi.common.util.csv.Querier;
import com.fulu.game.bi.entity.enums.OsEnum;
import com.fulu.game.bi.entity.enums.QueryTypeStatusEnum;
import com.fulu.game.bi.entity.po.channel.Label;
import com.fulu.game.bi.entity.po.channel.RegistSource;
import com.fulu.game.bi.entity.po.roi.NewAddUserStat;
import com.fulu.game.bi.entity.po.roi.NewUserRealtimeStat;
import com.fulu.game.bi.entity.vo.market.NewAddUserQueryVO;
import com.fulu.game.bi.hbase.HBaseNewAddUserStatDao;
import com.fulu.game.bi.mysql.bi.channel.LabelGroupDao;
import com.fulu.game.bi.mysql.bi.channel.RegisterSourceGroupDao;
import com.fulu.game.bi.mysql.bi.market.NewUserRealtimeStatDao;
import com.fulu.game.bi.mysql.playadmin.channel.LabelDao;
import com.fulu.game.bi.mysql.playadmin.channel.RegistSourceDao;
import com.fulu.game.bi.redis.service.market.NewAddUserService;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@Slf4j
public class NewAddUserServiceImpl implements NewAddUserService {

    private static final String PLACEHOLDER = "/";

    @Autowired
    private HBaseNewAddUserStatDao hBaseNewAddUserStatDao;
    @Autowired
    private RegistSourceDao registSourceDao;
    @Autowired
    private RegisterSourceGroupDao registerSourceGroupDao;
    @Autowired
    private LabelDao labelDao;
    @Autowired
    private LabelGroupDao labelGroupDao;
    @Autowired
    private NewUserRealtimeStatDao newUserRealtimeStatDao;


    public static final Map<Integer, String> tableNameMap = new HashMap();

    static {
        tableNameMap.put(QueryTypeStatusEnum.DAY.getType(), "new_add_user_stat_day");
        tableNameMap.put(QueryTypeStatusEnum.WEEK.getType(), "new_add_user_stat_week");
        tableNameMap.put(QueryTypeStatusEnum.MONTH.getType(), "new_add_user_stat_month");
    }

    @Override
    public List<NewAddUserStat> list(NewAddUserQueryVO vo) {

        // 装配渠道
        String registerSourceGroupName = PLACEHOLDER;
        if (Objects.nonNull(vo.getRegisterSourceGroupId()) && CollectionUtil.isEmpty(vo.getRegisterSourceId())) {
            List<Integer> sourceIdList = registerSourceGroupDao.listItem(vo.getRegisterSourceGroupId());
            if (CollectionUtil.isEmpty(sourceIdList)) {
                return new ArrayList<>();
            }
            registerSourceGroupName = registerSourceGroupDao.findNameById(vo.getRegisterSourceGroupId());

            vo.setRegisterSourceId(sourceIdList);
        }

        Map<Integer, String> sourceMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(vo.getRegisterSourceId())) {
            List<RegistSource> sourceList = registSourceDao.findByIds(vo.getRegisterSourceId()).stream()
                    .collect(Collectors.toList());
            List<Integer> platforms = sourceList.stream().map(RegistSource::getPlatform).collect(Collectors.toList());
            vo.setRegisterSourceId(platforms);
            sourceMap = sourceList.stream().collect(Collectors.toMap(RegistSource::getPlatform, RegistSource::getName));
        }

        // 装配标签
        String labelGroupName = PLACEHOLDER;
        if (Objects.nonNull(vo.getLabelGroupId()) && CollectionUtil.isEmpty(vo.getLabelIdList())) {
            List<Integer> labelIdList = labelGroupDao.listItem(vo.getLabelGroupId());
            if (CollectionUtil.isEmpty(labelIdList)) {
                return new ArrayList<>();
            }
            labelGroupName = labelGroupDao.findNameById(vo.getLabelGroupId());

            vo.setLabelIdList(labelIdList);
        }
        Map<Integer, String> labelMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(vo.getLabelIdList())) {
            labelMap = labelDao.findByIds(vo.getLabelIdList()).stream().collect(Collectors.toMap(Label::getId, Label::getName));
        }
        String registerTypeName = PLACEHOLDER;
        if (Objects.nonNull(vo.getRegisterType())) {
            registerTypeName = OsEnum.parse(vo.getRegisterType()).getMsg();
        }
        String tableName = tableNameMap.get(vo.getQueryType());
        Optional.ofNullable(tableName).orElseThrow(() -> new BizException("不支持的查询类型"));
        vo = handleQueryData(vo);
        List<NewAddUserStat> list = getResultList(vo, tableName);
        if (vo.isExportLabel()) {
            labelMap = labelDao.findByIds(list.stream().filter(map -> map.getLabelId() != null).map(map -> map.getLabelId()).collect(Collectors.toList())).stream().collect(Collectors.toMap(Label::getId, Label::getName));
        }
        if (vo.isExportSourceId()) {
            List<RegistSource> sourceList = registSourceDao.findByPlatforms(list.stream().filter(map -> map.getSourceId() != null).map(map -> map.getSourceId()).toArray(Integer[]::new));
            sourceMap = sourceList.stream().collect(Collectors.toMap(RegistSource::getPlatform, RegistSource::getName));
        }
        // 赋值、分组
        String finalRegisterSourceGroupName = registerSourceGroupName;
        String finalLabelGroupName = labelGroupName;
        Map<Integer, String> finalSourceMap = sourceMap;
        Map<Integer, String> finalLabelMap = labelMap;
        NewAddUserQueryVO finalVo = vo;
        String finalRegisterTypeName = registerTypeName;
        return list.stream()
                .peek(v -> v.setSourceGroupName(finalRegisterSourceGroupName))
                .peek(v -> v.setLabelGroupName(finalLabelGroupName))
                .peek(v -> setEndDateStr(finalVo, v))
                .peek(v -> {
                    if (v.getRegisterType() != null && finalVo.isExportRegisterType()) {
                        v.setRegisterTypeName(OsEnum.parse(v.getRegisterType()).getMsg());
                    } else {
                        v.setRegisterTypeName(finalRegisterTypeName);
                    }
                })
                .peek(v -> v.setBeginStatDay(v.getStatDate()))
                .peek(v -> setRate(v))
                .peek(v -> {
                    if (CollectionUtil.isEmpty(finalVo.getRegisterSourceId()) && !finalVo.isExportSourceId()) {
                        v.setSourceName(PLACEHOLDER);
                    } else {
                        v.setSourceName(Optional.ofNullable(finalSourceMap.get(v.getSourceId())).orElse(""));
                    }
                })
                .peek(v -> {
                    if (CollectionUtil.isEmpty(finalVo.getLabelIdList()) && !finalVo.isExportLabel()) {
                        v.setLabelName(PLACEHOLDER);
                    } else {
                        v.setLabelName(Optional.ofNullable(finalLabelMap.get(v.getLabelId())).orElse(""));
                    }
                })
                .peek(v -> v.setSourceId(v.getSourceId() == null ? 0 : v.getSourceId()))
                .sorted(Comparator.comparing(NewAddUserStat::getStatDate).reversed().thenComparing(NewAddUserStat::getSourceId))
                .collect(Collectors.toList());
    }

    private List<NewAddUserStat> getResultList(NewAddUserQueryVO vo, String tableName) {
        List<NewAddUserStat> list = hBaseNewAddUserStatDao.list(vo, tableName);
        //当天数据实时里面取
        if (Objects.equals(vo.getQueryType(), QueryTypeStatusEnum.DAY.getType())
                && DateUtil.beginOfDay(new Date()).getTime() <= vo.getStatTimeEnd().getTime()
                && DateUtil.beginOfDay(new Date()).getTime() >= vo.getStatTimeStart().getTime()) {
            list.removeIf(map -> Objects.equals(map.getStatDate(), DateUtil.formatDate(new Date())));
            vo.setStatTimeStartStr(DateUtil.formatDate(new Date()));
            vo.setStatTimeEndStr(DateUtil.formatDate(new Date()));
            list.addAll(hBaseNewAddUserStatDao.list(vo, "new_add_user_realtime_stat"));
        }
        return list;
    }

    private NewAddUserQueryVO handleQueryData(NewAddUserQueryVO vo) {
        QueryTypeStatusEnum queryTypeStatusEnum = QueryTypeStatusEnum.getEnum(vo.getQueryType());
        switch (queryTypeStatusEnum) {
            case DAY:
                vo.setStatTimeStartStr(vo.getStatTimeStart() == null ? null : DateUtil.formatDate(vo.getStatTimeStart()));
                vo.setStatTimeEndStr(vo.getStatTimeEnd() == null ? null : DateUtil.formatDate(vo.getStatTimeEnd()));
                break;
            case WEEK:
                vo.setStatTimeStartStr(vo.getStatTimeStart() == null ? null : DateUtil.formatDate(vo.getStatTimeStart()));
                vo.setStatTimeEndStr(vo.getStatTimeEnd() == null ? null : DateUtil.formatDate(DateUtil.offsetDay(vo.getStatTimeEnd(), -6)));
                break;
            case MONTH:
                vo.setStatTimeStartStr(vo.getStatTimeStart() == null ? null : DateUtil.format(vo.getStatTimeStart(), "yyyy-MM"));
                vo.setStatTimeEndStr(vo.getStatTimeEnd() == null ? null : DateUtil.format(vo.getStatTimeEnd(), "yyyy-MM"));
                break;
            default:
                break;
        }
        vo.setRegisterSourceIdStr(Objects.isNull(vo.getRegisterSourceId()) ? null : vo.getRegisterSourceId().stream().map(map -> map + "").collect(Collectors.toList()));
        vo.setLabelIdListStr(Objects.isNull(vo.getLabelIdList()) ? null : vo.getLabelIdList().stream().map(map -> map + "").collect(Collectors.toList()));
        vo.setRegisterTypeStr(Objects.isNull(vo.getRegisterType()) ? null : vo.getRegisterType().toString());
        return vo;
    }

    /**
     * 设置注册比率和付费比率
     */
    private void setRate(NewAddUserStat v) {
        v.setPayRate(radio(v.getInvestUserAll(), v.getNewRegistUserNum()));
        v.setLoginRate(radio(v.getNewRegistUserNum(), v.getNewActiveUserNum()));
    }

    protected void setEndDateStr(NewAddUserQueryVO vo, NewAddUserStat v) {
        if (QueryTypeStatusEnum.WEEK.getType().equals(vo.getQueryType())) {
            DateTime startTime = DateUtil.parse(v.getStatDate());
            DateTime endDateTime = DateUtil.offsetDay(startTime, 6);

            String statDateEnd = DateUtil.format(endDateTime, "yyyy-MM-dd");
            v.setEndStatDay(statDateEnd);
        } else {
            v.setEndStatDay(v.getStatDate());
        }
    }

    private Double radio(Integer s, Integer t) {
        if (Objects.isNull(t) || t == 0) {
            return 0d;
        }

        return BigDecimal.valueOf(s).divide(BigDecimal.valueOf(t), 2, BigDecimal.ROUND_HALF_DOWN).doubleValue();
    }

    @Override
    public void export(NewAddUserQueryVO vo) {
        String fileName = "新增用户" + vo.getStatTimeStart() + "-" + vo.getStatTimeEnd();

        LinkedHashMap<String, Mapper<NewAddUserStat>> map = new LinkedHashMap<>();
        map.put("日期", v -> v.getBeginStatDay() + "-" + v.getEndStatDay());
        map.put("渠道分组", v -> v.getSourceGroupName());
        map.put("渠道", v -> v.getSourceName());
        map.put("标签分组", v -> v.getLabelGroupName());
        map.put("标签", v -> v.getLabelName());
        map.put("系统", v -> v.getRegisterTypeName());

        map.put("新增激活用户数", v -> v.getNewActiveUserNum() + "");
        map.put("新增注册用户数", v -> v.getNewRegistUserNum() + "");
        map.put("注册转化率", v -> radio(v.getNewRegistUserNum(), v.getNewActiveUserNum()) + "");
        map.put("新增标签用户数", v -> v.getNewLabelUserNum() + "");

        map.put("充值总人数", v -> v.getInvestUserAll() + "");
        map.put("充值总次数", v -> v.getInvestNum() + "");
        map.put("充值总金额", v -> v.getInvestMoneyAll().doubleValue() + "");
        map.put("付费转化率", v -> radio(v.getInvestUserAll(), v.getNewRegistUserNum()) + "");

        map.put("皮皮币充值人数", v -> v.getPipiMoneyInvestUserNum() + "");
        map.put("皮皮币充值次数", v -> v.getPipiMoneyInvestNum() + "");
        map.put("皮皮币充值金额", v -> v.getPipiMoneyInvestAll() + "");

        map.put("钻石充值人数", v -> v.getVirtualMoneyInvestUserNum() + "");
        map.put("钻石充值次数", v -> v.getVirtualMoneyInvestNum() + "");
        map.put("钻石充值金额", v -> v.getVirtualMoneyInvestAll() + "");

        map.put("消费总人数", v -> v.getConsumeUserAll() + "");
        map.put("消费总次数", v -> v.getConsumeNum() + "");
        map.put("消费总金额", v -> v.getConsumeMoneyAll() + "");

        map.put("皮皮币消费人数", v -> v.getPipiMoneyConsumeUserNum() + "");
        map.put("皮皮币消费次数", v -> v.getPipiMoneyConsumeNum() + "");
        map.put("皮皮币消费金额", v -> v.getPipiMoneyConsumeAll() + "");

        map.put("钻石消费人数", v -> v.getVirtualMoneyConsumeUserNum() + "");
        map.put("钻石消费次数", v -> v.getVirtaulMoneyConsumeNum() + "");
        map.put("钻石消费金额", v -> v.getVirtualMoneyConsumeAll() + "");

        map.put("订单支付人数", v -> v.getOrderUserNum() + "");
        map.put("订单支付次数", v -> v.getOrderNum() + "");
        map.put("订单支付金额", v -> v.getOrderMoneyAll() + "");

        map.put("下单退款人数", v -> v.getRefundUserNum() + "");
        map.put("下单退款次数", v -> v.getRefundNum() + "");
        map.put("下单退款金额", v -> v.getRefundMoneyAll() + "");

        map.put("优惠券使用金额", v -> v.getCouponMoneyAll() + "");
        map.put("优惠券退款金额", v -> v.getRefundCouponMoneyAll() + "");

        map.put("皮皮币礼物消费人数", v -> v.getPipiGiftUserNum() + "");
        map.put("皮皮币礼物消费次数", v -> v.getPipiGiftNum() + "");
        map.put("皮皮币礼物消费金额", v -> v.getPipiGiftConsumeAll().toString());


        Querier querier = (num) -> {
            List<NewAddUserStat> list = list(vo);
            PageInfo<NewAddUserStat> page = new PageInfo<>(list);
            page.setHasNextPage(false);
            return page;
        };

        CsvExportUtil.export(100, fileName, map, querier);
    }

    private BigDecimal datio(Integer a, Integer b) {
        if (Objects.isNull(b) || b == 0) {
            return BigDecimal.ZERO;
        }
        return BigDecimal.valueOf((a) * 100).divide(BigDecimal.valueOf(b), 2, BigDecimal.ROUND_HALF_DOWN);
    }


    private BigDecimal ratio(Integer a, Integer b) {
        if (Objects.isNull(b) || b == 0) {
            return BigDecimal.ZERO;
        }
        return BigDecimal.valueOf((a - b) * 100).divide(BigDecimal.valueOf(b), 2, BigDecimal.ROUND_HALF_DOWN);
    }

    private BigDecimal ratio(BigDecimal a, BigDecimal b) {
        if (Objects.isNull(b) || b.intValue() == 0) {
            return BigDecimal.ZERO;
        }
        return BigDecimal.valueOf((a.intValue() - b.intValue()) * 100).divide(b, 2, BigDecimal.ROUND_HALF_DOWN);
    }

    @Override
    public NewUserRealtimeStat newUserRealtimeStatYest() {
        String yest = DateUtil.format(DateUtil.offsetDay(new Date(), -1), "yyyy-MM-dd");
        NewUserRealtimeStat yestDay = newUserRealtimeStatDao.findByStatTime(yest);
        log.info("昨日数据：{}", yestDay);

        if (Objects.isNull(yestDay)) {
            return new NewUserRealtimeStat();
        }

        String two = DateUtil.format(DateUtil.offsetDay(new Date(), -2), "yyyy-MM-dd");
        NewUserRealtimeStat twoDay = newUserRealtimeStatDao.findByStatTime(two);
        log.info("前日数据：{}", twoDay);

        String eight = DateUtil.format(DateUtil.offsetDay(new Date(), -8), "yyyy-MM-dd");
        NewUserRealtimeStat eightDay = newUserRealtimeStatDao.findByStatTime(eight);
        log.info("上周数据：{}", eightDay);

        if (Objects.nonNull(twoDay)) {
            yestDay.setActiveUserNumYest(ratio(yestDay.getActiveUserNum(), twoDay.getActiveUserNum()));
            yestDay.setRegistUserNumYest(ratio(yestDay.getRegistUserNum(), twoDay.getRegistUserNum()));
            yestDay.setNewUserInvestNumYest(ratio(yestDay.getNewUserInvestNum(), twoDay.getNewUserInvestNum()));
            yestDay.setNewUserInvestMoneyAllYest(ratio(yestDay.getNewUserInvestMoneyAll(), twoDay.getNewUserInvestMoneyAll()));
            yestDay.setNewUserConsumeNumYest(ratio(yestDay.getNewUserConsumeNum(), twoDay.getNewUserConsumeNum()));
            yestDay.setNewUserConsumeMoneyAllYest(ratio(yestDay.getNewUserConsumeMoneyAll(), twoDay.getNewUserConsumeMoneyAll()));

            yestDay.setRegisterPercentYest(ratio(datio(yestDay.getRegistUserNum(), yestDay.getActiveUserNum()), datio(twoDay.getRegistUserNum(), twoDay.getActiveUserNum())));
            yestDay.setPayPercentYest(ratio(datio(yestDay.getNewUserInvestNum(), yestDay.getActiveUserNum()), datio(twoDay.getNewUserInvestNum(), twoDay.getActiveUserNum())));
        }

        if (Objects.nonNull(eightDay)) {
            yestDay.setActiveUserNumWeek(ratio(yestDay.getActiveUserNum(), eightDay.getActiveUserNum()));
            yestDay.setRegistUserNumWeek(ratio(yestDay.getRegistUserNum(), eightDay.getRegistUserNum()));
            yestDay.setNewUserInvestNumWeek(ratio(yestDay.getNewUserInvestNum(), eightDay.getNewUserInvestNum()));
            yestDay.setNewUserInvestMoneyAllWeek(ratio(yestDay.getNewUserInvestMoneyAll(), eightDay.getNewUserInvestMoneyAll()));
            yestDay.setNewUserConsumeNumWeek(ratio(yestDay.getNewUserConsumeNum(), eightDay.getNewUserConsumeNum()));
            yestDay.setNewUserConsumeMoneyAllWeek(ratio(yestDay.getNewUserConsumeMoneyAll(), eightDay.getNewUserConsumeMoneyAll()));

            yestDay.setRegisterPercentWeek(ratio(datio(yestDay.getRegistUserNum(), yestDay.getActiveUserNum()), datio(eightDay.getRegistUserNum(), eightDay.getActiveUserNum())));
            yestDay.setPayPercentWeek(ratio(datio(yestDay.getNewUserInvestNum(), yestDay.getActiveUserNum()), datio(eightDay.getNewUserInvestNum(), eightDay.getActiveUserNum())));
        }

        return yestDay;
    }

    @Override
    public NewUserRealtimeStat newUserRealtimeStat() {
        String statTime = DateUtil.format(new Date(), "yyyy-MM-dd");
        return newUserRealtimeStatDao.findByStatTime(statTime);
    }
}
